    /*
 * Copyright (c) 2022, yifengling0. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. No personal names or organizations' names associated with the
 *    Atomthreads project may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE ATOMTHREADS PROJECT AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "iosdrv.h"

#include <string.h>

#include "atom.h"
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
#include "pin.h"

#define _STM32_PIN(index, gpio, gpio_index)                                \
    {                                                                       \
        index, GPIO##gpio, GPIO##gpio_index                            \
    }

/* STM32 GPIO driver */
struct pin_index
{
    int index;
    uint32_t gpio;
    int pin;
};

static const struct pin_index pins[] = 
{
    _UPBOARD_PIN(0 ,  A, 0 ),
    _UPBOARD_PIN(1 ,  A, 1 ),
    _UPBOARD_PIN(2 ,  A, 2 ),
    _UPBOARD_PIN(3 ,  A, 3 ),
    _UPBOARD_PIN(4 ,  A, 4 ),
    _UPBOARD_PIN(5 ,  A, 5 ),
    _UPBOARD_PIN(6 ,  A, 6 ),
    _UPBOARD_PIN(7 ,  A, 7 ),
    _UPBOARD_PIN(8 ,  A, 8 ),
    _UPBOARD_PIN(9 ,  A, 9 ),
    _UPBOARD_PIN(10,  A, 10),
    _UPBOARD_PIN(11,  A, 11),
    _UPBOARD_PIN(12,  A, 12),
    _UPBOARD_PIN(13,  A, 13),
    _UPBOARD_PIN(14,  A, 14),
    _UPBOARD_PIN(15,  A, 15),
    _UPBOARD_PIN(16 ,  B, 0 ),
    _UPBOARD_PIN(17 ,  B, 1 ),
    _UPBOARD_PIN(18 ,  B, 2 ),
    _UPBOARD_PIN(19 ,  B, 3 ),
    _UPBOARD_PIN(20 ,  B, 4 ),
    _UPBOARD_PIN(21 ,  B, 5 ),
    _UPBOARD_PIN(22 ,  B, 6 ),
    _UPBOARD_PIN(23 ,  B, 7 ),
    _UPBOARD_PIN(24 ,  B, 8 ),
    _UPBOARD_PIN(25 ,  B, 9 ),
    _UPBOARD_PIN(26 ,  B, 10),
    _UPBOARD_PIN(27 ,  B, 11),
    _UPBOARD_PIN(28 ,  B, 12),
    _UPBOARD_PIN(29 ,  B, 13),
    _UPBOARD_PIN(30 ,  B, 14),
    _UPBOARD_PIN(31 ,  B, 15),
    _UPBOARD_PIN(32 ,  C, 0 ),    
    _UPBOARD_PIN(33 ,  C, 1 ),
    _UPBOARD_PIN(34 ,  C, 2 ),
    _UPBOARD_PIN(35 ,  C, 3 ),
    _UPBOARD_PIN(36 ,  C, 4 ),
    _UPBOARD_PIN(37 ,  C, 5 ),
    _UPBOARD_PIN(38 ,  C, 6 ),
    _UPBOARD_PIN(39 ,  C, 7 ),
    _UPBOARD_PIN(40 ,  C, 8 ),
    _UPBOARD_PIN(41 ,  C, 9 ),
    _UPBOARD_PIN(42 ,  C, 10),
    _UPBOARD_PIN(43 ,  C, 11),
    _UPBOARD_PIN(44 ,  C, 12),
    _UPBOARD_PIN(45 ,  C, 13),
    _UPBOARD_PIN(46 ,  C, 14),
    _UPBOARD_PIN(47 ,  C, 15),
    
};

    
#define ITEM_NUM(items) sizeof(items) / sizeof(items[0])
static const struct pin_index *get_pin(uint8_t pin)
{
    const struct pin_index *index;

    if (pin < ITEM_NUM(pins))
    {
        index = &pins[pin];
        if (index->index == -1)
            index = NULL;
    }
    else
    {
        index = NULL;
    }

    return index;
};


int  upboard_read( const Pin* dev, int pin )
{
    int value;
    const struct pin_index *index;

    value = PIN_LOW;

    index = get_pin(pin);
    if (index == NULL)
    {
        return value;
    }

    value = gpio_port_read(index->gpio) & index->pin;
    
    if(value > 0) return HIGH;
    else return LOW;
}


void upboard_write( const Pin* dev, int pin, int value )
{
    const struct pin_index *index;

    index = get_pin(pin);
    if (index == NULL)
    {
        return;
    }

    if( value == HIGH ){
        gpio_set(index->gpio, index->pin);
    }else{
        gpio_clear(index->gpio, index->pin);
    }
}

void upboard_toggle( const Pin* dev, int pin)
{
    const struct pin_index *index;

    index = get_pin(pin);
    if (index == NULL)
    {
        return;
    }

    gpio_toggle(index->gpio, index->pin);
}

void upboard_mode( const Pin* dev, int pin, int mode)
{
    const struct pin_index *index;
    int mode_type = GPIO_MODE_INPUT;
    int pull_up_down = GPIO_PUPD_NONE;


    index = get_pin(pin);
    if (index == NULL)
    {
        return;
    }

    if (mode == PIN_MODE_OUTPUT)
    {
        /* output setting */
        mode_type = GPIO_MODE_OUTPUT;
    }
    else if (mode == PIN_MODE_INPUT)
    {
        /* input setting: not pull. */
        mode_type = GPIO_MODE_INPUT;
    }
    else if (mode == PIN_MODE_INPUT_PULLUP)
    {
        mode_type =  GPIO_MODE_INPUT;
        /* input setting: pull up. */
    }
    else if (mode == PIN_MODE_INPUT_PULLDOWN)
    {
        mode_type =  GPIO_MODE_INPUT;
        /* input setting: pull down. */
    }

    else if (mode == PIN_MODE_OUTPUT_OD)
    {
        mode_type = GPIO_MODE_OUTPUT;
        /* output setting: od. */
    }


    gpio_mode_setup(index->gpio, mode_type, pull_up_down, index->pin);
}


static struct PinOps _pin_ops = {
    upboard_read,
    upboard_write,
    upboard_toggle,
    upboard_mode
};

void Stm32PinCreate(void)
{
    PinCreate("pin", &_pin_ops);
}